﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace AwesomeEnginePC.Physics
{
    public class Vector
    {
        #region Private Vars

        private double _len;
        private Vector _normal;
        private double _x;
        private double _y;
        private bool bInvalidLen;
        private bool bInvalidNormal;

        #endregion

        #region Properties

        /// <summary>
        /// The length of the vector.
        /// </summary>
        public double Length
        {
            get
            {
                if (bInvalidLen)
                {
                    UpdateLen();
                }
                return _len;
            }
        }

        /// <summary>
        /// A vector representing the normal of the current vector.
        /// </summary>
        public Vector Normal
        {
            get
            {
                if (bInvalidNormal)
                {
                    UpdateNormal();
                }
                return _normal;
            }
        }

        /// <summary>
        /// The x component of the vector.
        /// </summary>
        public double X
        {
            get { return _x; }
            set { _x = value; }
        }
        /// <summary>
        /// The y component of the vector.
        /// </summary>
        public double Y
        {
            get { return _y; }
            set { _y = value; }
        }

        #endregion

        /// <summary>
        /// Creates a new Vector object with default values.
        /// </summary>
        public Vector()
        {
            _x = 0;
            _y = 0;

            bInvalidLen = true;
            bInvalidNormal = true;
        }

        /// <summary>
        /// Creates a new Vector object with custom values.
        /// </summary>
        /// <param name="x">The x component of the vector.</param>
        /// <param name="y">The y component of the vector.</param>
        public Vector(double x, double y)
        {
            SetVector(x, y);
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #region Public Methods

        /// <summary>
        /// Returns a Vector that is inverted from the current Vector. Does not change the current Vector.
        /// </summary>
        /// <returns></returns>
        public Vector GetInverted()
        {
            return new Vector(-X, -Y);
        }

        /// <summary>
        /// Returns a Vector that is a normal from the current Vector. Does not change the current Vector.
        /// </summary>
        /// <returns></returns>
        public Vector GetNormalized()
        {
            if (Length == 0)
            {
                return null;
            }
            else
            {
                return new Vector(X / Length, Y / Length);
            }
        }

        /// <summary>
        /// Inverts the current vector. Does not return anything.
        /// </summary>
        public void Invert()
        {
            X *= -1;
            Y *= -1;
            bInvalidNormal = true;
        }

        /// <summary>
        /// Normalizes the current vector. Does not return anything.
        /// </summary>
        public void Normalize()
        {
            if (Length == 0)
            {
                X = 0;
                Y = 0;
            }
            else
            {
                X /= Length;
                Y /= Length;
            }
            bInvalidLen = true;
        }

        /// <summary>
        /// Sets the values of the vector.
        /// </summary>
        /// <param name="x">The x component of the vector.</param>
        /// <param name="y">The y component of the vector.</param>
        public void SetVector(double x, double y)
        {
            _x = x;
            _y = y;
            bInvalidLen = true;
            bInvalidNormal = true;
        }

        /// <summary>
        /// Converts the Vector object to a Vector2 object.
        /// </summary>
        /// <returns></returns>
        public Vector2 ToVector2()
        {
            return new Vector2((float)_x, (float)_y);
        }
        /// <summary>
        /// Updates the Length of the vector.
        /// </summary>
        private void UpdateLen()
        {
            _len = Math.Sqrt(X * X + Y * Y);
            bInvalidLen = false;
        }

        /// <summary>
        /// Updates the normal of the vector.
        /// </summary>
        private void UpdateNormal()
        {
            _normal = new Vector(-Y / Length, X / Length);
            bInvalidNormal = false;
        }

        #endregion

        #region Operators

        public static Vector operator -(Vector vector, double num)
        {
            return new Vector(vector.X - num, vector.Y - num);
        }

        public static Vector operator -(Vector vector, int num)
        {
            return new Vector(vector.X - num, vector.Y - num);
        }

        public static Vector operator -(Vector vector, float num)
        {
            return new Vector(vector.X - num, vector.Y - num);
        }

        public static Vector operator -(Vector left, Vector right)
        {
            return new Vector(left.X - right.X, left.Y - right.Y);
        }

        public static bool operator !=(Vector left, Vector right)
        {
            if ((left.X == right.X) && (left.Y == right.Y))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        public static Vector operator *(Vector vector, double num)
        {
            return new Vector(vector.X * num, vector.Y * num);
        }

        public static Vector operator *(Vector vector, float num)
        {
            return new Vector(vector.X * num, vector.Y * num);
        }

        public static Vector operator *(Vector vector, int num)
        {
            return new Vector(vector.X * num, vector.Y * num);
        }

        public static Vector operator *(Vector left, Vector right)
        {
            return new Vector(left.X * right.X, left.Y * right.Y);
        }

        public static Vector operator /(Vector vector, double num)
        {
            return new Vector(vector.X / num, vector.Y / num);
        }

        public static Vector operator /(Vector vector, int num)
        {
            return new Vector(vector.X / num, vector.Y / num);
        }

        public static Vector operator /(Vector vector, float num)
        {
            return new Vector(vector.X / num, vector.Y / num);
        }

        public static Vector operator /(Vector left, Vector right)
        {
            return new Vector(left.X / right.X, left.Y / right.Y);
        }

        public static Vector operator +(Vector vector, double num)
        {
            return new Vector(vector.X + num, vector.Y + num);
        }

        public static Vector operator +(Vector vector, int num)
        {
            return new Vector(vector.X + num, vector.Y + num);
        }

        public static Vector operator +(Vector vector, float num)
        {
            return new Vector(vector.X + num, vector.Y + num);
        }

        public static Vector operator +(Vector left, Vector right)
        {
            return new Vector(left.X + right.X, left.Y + right.Y);
        }

        public static bool operator ==(Vector left, Vector right)
        {
            if ((left.X == right.X) && (left.Y == right.Y))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Performs a dot product on the current vector and the inputted vector.
        /// </summary>
        /// <param name="v">Another vector to perform the operation with.</param>
        /// <example>With two vectors {3,4} and {2,5}, the dot product would be (3*2) + (4*5) = 26.</example>
        /// <returns></returns>
        public double DotProduct(Vector v)
        {
            return X * v.X + Y * v.Y;
        }
        #endregion

        public override string ToString()
        {
            return "{ " + X + " : " + Y + " }";
        }
    }
}
